import pandas as pd
import os
import shutil
from datetime import datetime
import tkinter as tk
from tkinter import messagebox
import logging

# 定义文件路径
库存文件 = '库存.csv'
出入库记录文件 = '出入库记录.csv'
入库目录 = '入库目录'
出库目录 = '出库目录'
归档目录 = '归档目录'
库存备份目录 = os.path.join(归档目录, '库存备份')
入库归档目录 = os.path.join(归档目录, '入库')
出库归档目录 = os.path.join(归档目录, '出库')
出库原文件目录 = os.path.join(归档目录, '原文件', '出库')
入库原文件目录 = os.path.join(归档目录, '原文件', '入库')
商品信息文件 = '商品信息.xlsx'

# 初始化日志记录
logging.basicConfig(filename='inventory.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 初始化库存表和出入库表
def 初始化库存():
    global inventory_df, transactions_df, 商品信息_df
    
    if not os.path.exists(库存文件):
        initial_inventory = {
            '商品编号': [],
            '商品系列': [],
            '商品名称': [],
            '数量': [],
            '更新时间': []
        }
        inventory_df = pd.DataFrame(initial_inventory)
        写入_csv(inventory_df, 库存文件)
    else:
        inventory_df = 读取_csv(库存文件)

    if not os.path.exists(出入库记录文件):
        transactions_df = pd.DataFrame(columns=['商品编号', '商品系列', '商品名称', '出入库方向', '计划数量', '实出数量', '缺货数量', '变更后的库存', '更新时间'])
        写入_csv(transactions_df, 出入库记录文件)
    else:
        transactions_df = 读取_csv(出入库记录文件)
    
    商品信息_df = 读取_excel(商品信息文件)

# 确保目录存在
def 确保目录存在():
    os.makedirs(入库目录, exist_ok=True)
    os.makedirs(出库目录, exist_ok=True)
    os.makedirs(库存备份目录, exist_ok=True)
    os.makedirs(入库归档目录, exist_ok=True)
    os.makedirs(出库归档目录, exist_ok=True)
    os.makedirs(入库原文件目录, exist_ok=True)
    os.makedirs(出库原文件目录, exist_ok=True)

# 读取CSV文件
def 读取_csv(文件路径):
    try:
        df = pd.read_csv(文件路径, encoding='utf-8')
    except:
        df = pd.read_csv(文件路径, encoding='gbk')
    
    # 验证数据格式
    # required_columns = ['商品编号', '商品系列', '商品名称', '数量', '更新时间']
    # if not all(column in df.columns for column in required_columns):
    #     raise ValueError(f"CSV文件缺少必需的列: {required_columns}")
    
    # if not all(isinstance(x, (int, str)) for x in df['商品编号']):
    #     raise ValueError("商品编号必须是数字或字符串")
    
    # if not all(isinstance(x, str) for x in df['商品系列']):
    #     raise ValueError("商品系列必须是字符串")
    
    # if not all(isinstance(x, str) for x in df['商品名称']):
    #     raise ValueError("商品名称必须是字符串")
    
    # if not all(isinstance(x, (int, float)) for x in df['数量']):
    #     raise ValueError("数量必须是数字")
    
    return df

# 读取Excel文件
def 读取_excel(文件路径):
    try:
        df = pd.read_excel(文件路径)
    except:
        raise ValueError("无法读取Excel文件")
    
    # 验证数据格式
    # required_columns = ['商品编号', '商品全名', '商品系列', '商品名称']
    # if not all(column in df.columns for column in required_columns):
    #     raise ValueError(f"Excel文件缺少必需的列: {required_columns}")
    
    # if not all(isinstance(x, (int, str)) for x in df['商品编号']):
    #     raise ValueError("商品编号必须是数字或字符串")
    
    # if not all(isinstance(x, str) for x in df['商品全名']):
    #     raise ValueError("商品全名必须是字符串")
    
    # if not all(isinstance(x, str) for x in df['商品系列']):
    #     raise ValueError("商品系列必须是字符串")
    
    # if not all(isinstance(x, str) for x in df['商品名称']):
    #     raise ValueError("商品名称必须是字符串")
    
    return df

# 写入CSV文件
def 写入_csv(df, 文件路径):
    df.to_csv(文件路径, index=False)

# 根据商品系列和商品名称获取商品编号
def 获取商品信息(商品系列, 商品名称):
    global 商品信息_df
    match = 商品信息_df[(商品信息_df['商品系列'] == 商品系列) & (商品信息_df['商品名称'] == 商品名称)]
    if not match.empty:
        return match.iloc[0]
    else:
        logging.error(f"未找到商品系列: {商品系列}, 商品名称: {商品名称}")
        print(f"未找到商品系列: {商品系列}, 商品名称: {商品名称}")
        return None

# 更新库存
def 更新库存(商品编号, 商品系列, 商品名称, 计划数量, 出入库方向):
    global inventory_df, transactions_df
    
    product_index = inventory_df[inventory_df['商品编号'] == 商品编号].index
    if len(product_index) == 0:
        new_product = {'商品编号': 商品编号, '商品系列': 商品系列, '商品名称': 商品名称, '数量': 0}
        inventory_df = pd.concat([inventory_df, pd.DataFrame([new_product])], ignore_index=True)
        product_index = inventory_df[inventory_df['商品编号'] == 商品编号].index
        当前库存 = 0
    else:
        当前库存 = inventory_df.at[product_index[0], '数量']
    
    if 出入库方向 == '入库':
        新库存 = 当前库存 + 计划数量
        实出数量 = 计划数量
        缺货数量 = 0
    elif 出入库方向 == '出库':
        新库存 = max(0, 当前库存 - 计划数量)
        实出数量 = min(计划数量, 当前库存)
        缺货数量 = 计划数量 - 实出数量
    
    inventory_df.at[product_index[0], '数量'] = 新库存
    inventory_df.at[product_index[0], '更新时间'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    
    出入库记录 = {
        '商品编号': 商品编号,
        '商品系列': 商品系列,
        '商品名称': 商品名称,
        '出入库方向': 出入库方向,
        '计划数量': 计划数量,
        '实出数量': 实出数量,
        '缺货数量': 缺货数量,
        '变更后的库存': 新库存,
        '更新时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    }
    transactions_df = pd.concat([transactions_df, pd.DataFrame([出入库记录])], ignore_index=True)
    
    写入_csv(inventory_df, 库存文件)
    写入_csv(transactions_df, 出入库记录文件)
    
    logging.info(f"出入库记录: {出入库记录}")
    print(f"出入库记录: {出入库记录}")

# 批量更新库存
def 批量更新库存(目录路径, 出入库方向):
    global inventory_df, transactions_df
    
    csv_files = [f for f in os.listdir(目录路径) if f.endswith('.csv')]
    if not csv_files:
        print(f"{目录路径} 目录下没有 CSV 文件。")
        return
    
    backup_file_name = f"库存_{datetime.now().strftime('%Y%m%d%H%M%S')}.csv"
    backup_path = os.path.join(库存备份目录, backup_file_name)
    shutil.copy(库存文件, backup_path)
    print(f"库存表已备份到: {backup_path}")
    
    actual_operations_df = pd.DataFrame(columns=['商品编号', '商品系列', '商品名称', '数量'])
    shortage_df = pd.DataFrame(columns=['商品编号', '商品系列', '商品名称', '计划数量', '缺货数量'])
    
    for 文件名 in csv_files:
        文件路径 = os.path.join(目录路径, 文件名)
        operation_df = 读取_csv(文件路径)
        
        for index, row in operation_df.iterrows():
            商品系列 = row.get('商品系列')
            商品名称 = row.get('商品名称')
            计划数量 = row.get('数量')
            
            if 商品系列 is None or 商品名称 is None or 计划数量 is None:
                logging.error(f"CSV文件缺少必需的列: 商品系列, 商品名称, 数量")
                print(f"CSV文件缺少必需的列: 商品系列, 商品名称, 数量")
                continue
            
            商品信息 = 获取商品信息(商品系列, 商品名称)
            if 商品信息 is None:
                continue
            
            商品编号 = 商品信息['商品编号']
            商品系列 = 商品信息['商品系列']
            商品名称 = 商品信息['商品名称']
            
            product_index = inventory_df[inventory_df['商品编号'] == 商品编号].index
            if len(product_index) == 0:
                new_product = {'商品编号': 商品编号, '商品系列': 商品系列, '商品名称': 商品名称, '数量': 0}
                inventory_df = pd.concat([inventory_df, pd.DataFrame([new_product])], ignore_index=True)
                product_index = inventory_df[inventory_df['商品编号'] == 商品编号].index
                当前库存 = 0
            else:
                当前库存 = inventory_df.at[product_index[0], '数量']
            
            if 出入库方向 == '入库':
                新库存 = 当前库存 + 计划数量
                实出数量 = 计划数量
                缺货数量 = 0
            elif 出入库方向 == '出库':
                新库存 = max(0, 当前库存 - 计划数量)
                实出数量 = min(计划数量, 当前库存)
                缺货数量 = 计划数量 - 实出数量
            
            inventory_df.at[product_index[0], '数量'] = 新库存
            inventory_df.at[product_index[0], '更新时间'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            
            出入库记录 = {
                '商品编号': 商品编号,
                '商品系列': 商品系列,
                '商品名称': 商品名称,
                '出入库方向': 出入库方向,
                '计划数量': 计划数量,
                '实出数量': 实出数量,
                '缺货数量': 缺货数量,
                '变更后的库存': 新库存,
                '更新时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            }
            transactions_df = pd.concat([transactions_df, pd.DataFrame([出入库记录])], ignore_index=True)
            
            print(f"出入库记录: {出入库记录}")
            
            if 出入库方向 == '入库':
                actual_operations_df = pd.concat([actual_operations_df, pd.DataFrame([{'商品编号': 商品编号, '商品系列': 商品系列, '商品名称': 商品名称, '出入数量': 计划数量}])], ignore_index=True)
            elif 出入库方向 == '出库':
                if 当前库存 < 计划数量:
                    shortage_df = pd.concat([shortage_df, pd.DataFrame([{'商品编号': 商品编号, '商品系列': 商品系列, '商品名称': 商品名称, '计划数量': 计划数量, '缺货数量': 缺货数量}])], ignore_index=True)
                else:
                    actual_operations_df = pd.concat([actual_operations_df, pd.DataFrame([{'商品编号': 商品编号, '商品系列': 商品系列, '商品名称': 商品名称, '出入数量': 计划数量}])], ignore_index=True)
        
        shutil.move(文件路径, os.path.join(入库原文件目录 if 出入库方向 == '入库' else 出库原文件目录, datetime.now().strftime('%Y%m%d%H%M%S') + 文件名))
        print(f"文件 '{文件名}' 已移动到归档目录。")
    
    if not actual_operations_df.empty:
        file_name_prefix = "实入" if 出入库方向 == '入库' else "实出"
        file_name = f"{file_name_prefix}_{datetime.now().strftime('%Y%m%d%H%M%S')}.csv"
        file_path = os.path.join(入库归档目录 if 出入库方向 == '入库' else 出库归档目录, file_name)
        写入_csv(actual_operations_df, file_path)
        print(f"{file_name_prefix}记录已保存到: {file_path}")
    
    if not shortage_df.empty:
        shortage_file_name = f"缺货_{datetime.now().strftime('%Y%m%d%H%M%S')}.csv"
        shortage_path = os.path.join(出库归档目录, shortage_file_name)
        写入_csv(shortage_df, shortage_path)
        print(f"缺货记录已保存到: {shortage_path}")
    
    写入_csv(inventory_df, 库存文件)
    写入_csv(transactions_df, 出入库记录文件)

# 显示库存
def 显示库存():
    print("\n当前库存:")
    print(inventory_df)

# 显示出入库记录
def 显示出入库记录():
    print("\n出入库记录历史:")
    print(transactions_df)

# GUI界面
def gui_批量入库():
    批量更新库存(入库目录, '入库')
    messagebox.showinfo("提示", "批量入库完成")

def gui_批量出库():
    批量更新库存(出库目录, '出库')
    messagebox.showinfo("提示", "批量出库完成")

def gui_显示库存():
    显示库存()
    messagebox.showinfo("当前库存", inventory_df.to_string(index=False))

def gui_显示出入库记录():
    显示出入库记录()
    messagebox.showinfo("出入库记录", transactions_df.to_string(index=False))

def main_gui():
    root = tk.Tk()
    root.title("库存管理系统")
    
    tk.Button(root, text="批量入库", command=gui_批量入库).pack(pady=10)
    tk.Button(root, text="批量出库", command=gui_批量出库).pack(pady=10)
    tk.Button(root, text="显示库存", command=gui_显示库存).pack(pady=10)
    tk.Button(root, text="显示出入库记录", command=gui_显示出入库记录).pack(pady=10)
    tk.Button(root, text="退出", command=root.quit).pack(pady=10)
    
    root.mainloop()

# 主函数
def main():
    初始化库存()
    确保目录存在()
    main_gui()

if __name__ == '__main__':
    main()
